/**
	Copyright (c) 2009 Ozazo Pvt Ltd, INDIA

	Permission is hereby granted, free of charge, to any person or organization 
	obtaining a copy of this software and associated  documentation  files (the 
	"Software"), to deal in the Software without restriction, including without 
	limitation the rights to use, copy, modify, merge, publish, distribute, sub
	license, and/or sell copies of the Software, and to permit persons  to whom 
	the Software is furnished to do so, subject to the following conditions:
	
	The above copyright notice and  this permission notice shall be included in 
	all copies or substantial portions of the Software.
	
	THE SOFTWARE IS PROVIDED "AS IS",  WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING  BUT NOT  LIMITED  TO THE WARRANTIES OF MERCHANTABILITY,
	FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
	AUTHORS OR  COPYRIGHT  HOLDERS  BE  LIABLE  FOR ANY CLAIM, DAMAGES OR OTHER
	LIABILITY,  WHETHER  IN  AN  ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
	FROM,  OUT  OF  OR  IN  CONNECTION  WITH  THE  SOFTWARE OR THE USE OR OTHER 
	DEALINGS IN THE SOFTWARE
 */

package com.ozazo.wave.robots.engines;

import java.io.*;
import java.net.*;
import java.util.*;
import java.util.logging.Logger;

import com.google.wave.api.Annotation;
import com.google.wave.api.Blip;
import com.google.wave.api.Image;
import com.google.wave.api.Range;
import com.google.wave.api.TextView;
import com.ozazo.wave.robots.fw.engines.AbstractRobotEngine;
import com.ozazo.wave.robots.fw.engines.RobotEngineInterface;
import com.ozazo.wave.robots.fw.engines.Commands;
import com.ozazo.wave.robots.fw.search.OpenSearchResult;
import com.ozazo.wave.robots.fw.utils.OpenSearchResultSAXParser;

/**
 * WikiPedia Engine
 * 
 * @author Araf Karsh Hamid
 * @version 1.0
 * @seriel 20091121102030
 */

public class WikiEngine implements RobotEngineInterface {
	
	private final Logger log;

	private String wikiURL 						= "http://%s.wikipedia.org/w/api.php?action=opensearch&search=%s&format=xml&limit=%s";

	private final String regexCommands 			= "(?ism)[-/](w|wd)\\b";
	
	private final String commands[] 			= { "/w", "-w", "/wd", "-wd" };
	
	public static final String DEFAULT_LANGUAGE = "en";
	public static final String RESULT_SIZE		= "2";
	public static final String SEARCH_LIMIT		= "100";

	private String searchQuery 					= "Kerala";
	private String language						= DEFAULT_LANGUAGE;

	private BufferedReader wikiReader			= null;
	private boolean isModified					= false;
	
	public WikiEngine() {
		this(Logger.getLogger("WikiEngine"));
	}
	
	public WikiEngine(Logger _log) {
		log = _log;
	}

	/**
	 * Set the Search Query. Query String can be of single word or multiple words.
	 * 
	 * @param _searchQuery
	 */
	public void setSearchQuery(final String _searchQuery) {
		if( (_searchQuery != null) && (!_searchQuery.trim().equals("")) ) {
			searchQuery = _searchQuery.replace(" ","%20");
		}	
	}
	
	/**
	 * Set the query language. Default language is English
	 * 
	 * @param _language
	 */

	public void setLanguage(final String _language) {
		if( (_language != null) && (!_language.trim().equals("")) ) {
			language = _language;
		}
	}
	
	/**
	 * Get the Query String to search.
	 * 
	 * @return String
	 */

	private String getResultSearchString() {
		return String.format(wikiURL, language, searchQuery, RESULT_SIZE);
	}

	/**
	 * Get the Query String for Link.
	 * 
	 * @return
	 */
	private String getLinkSearchString() {
		return String.format(wikiURL, language, searchQuery, SEARCH_LIMIT);
	}

	/**
	 * Opens the InputStream (Reader) from the Wiki Site using the Query String.
	 * 
	 * @return BufferedReader
	 * @throws Exception
	 */
	public BufferedReader openWikiReader() throws Exception {
		if(wikiReader == null) {
			URL url = new URL(getResultSearchString());
			wikiReader = new BufferedReader(new InputStreamReader(url.openStream(), "UTF-8"));
		}
		return wikiReader;
	}

	/**
	 * Close the Reader (connected to the Wiki site)
	 */
	public void closeReader() {
		if(wikiReader != null) {
			try {
				wikiReader.close();
			} catch (Exception ignored) {}
			wikiReader = null;
		}
	}

	/**
	 * Get Search Result will 
	 * 1. Connect to the Wiki Site and 
	 * 2. Parse the Return XML
	 * 3. Construct OpenSearchResult Object structure.
	 * 
	 * @return OpenSearchResult
	 */
	public OpenSearchResult getSearchResult() {
		OpenSearchResult result = new OpenSearchResult("EMPTY");
		try {
			OpenSearchResultSAXParser parser = new OpenSearchResultSAXParser();
			result = parser.parse(openWikiReader());
		} catch (Exception e) {
		} finally {
			closeReader();
		}
		return result;
	}

	protected void finalize() {
		closeReader();
	}

	public final String getURL(final String _text) {
		if(_text == null) {
			return _text;
		}
		String url = null;
		setSearchQuery(_text.replace("\"", ""));
		OpenSearchResult result = getSearchResult();
		if( (result != null) && (result.isResultAvailable()) ) {
			url = result.getFirstResult().getURL();
		}
		return url;
		
	}
	
	/**
	 * From the list of String Tokens - Create the URL
	 *
	 * @param List<Commands>
	 * @return List<Commands>
	 */	
	
	public List<Commands> processCommands(List<Commands> _list) {
		List<Commands> cmdList = new ArrayList<Commands>();
		if(_list == null) {
			return cmdList;
		}
		for(Commands cmd : _list) {
			if(cmd.getCommand().equalsIgnoreCase("w")) {
				setSearchQuery(cmd.getValue().trim()); 	
				OpenSearchResult result = getSearchResult();
				if( (result != null) && (result.isResultAvailable()) ) {
						cmd.setExternalData(result.getFirstResult().getURL());
						cmdList.add(cmd);
				} else {
					// 	This sets the flag foundResult to FALSE
					cmd.setNoResult();		
					cmdList.add(cmd);					
				}
			} else if(cmd.getCommand().equalsIgnoreCase("wd")) {
				setSearchQuery(cmd.getValue().trim()); 	
				OpenSearchResult result = getSearchResult();
				if( (result != null) && (result.isResultAvailable()) ) {
						cmd.setExternalData(result.getFirstResult().getDescription());					
						cmdList.add(cmd);
				} else {
					// 	This sets the flag foundResult to FALSE
					cmd.setNoResult();		
					cmdList.add(cmd);
				}
			}
		}
		return cmdList;
	}
	
	/**
	 * This method illustrates how to replace the text in Blip without using TextView.replace(String) method.
	 * The above looses all the styles and annotations of the TextView. This method uses the following functionality
	 * from the TextView object in the Google Wave.
	 * 
	 * 1. Uses Range and Annotations to insert the text in the right place.
	 * 2. Uses Annotations to create hyper links.
	 * 3. Uses replace with range parameter to edit specific substring of the text in the TextView.
	 * 
	 * @param Blip _blip
 	 * @param Map<String, Object> dataMap
	 */
	
	public void processBlip(Blip _blip, List<Commands> dataMap) {
		if( (_blip == null) || (_blip.getDocument().getText().trim().equals("")))  {
			return;
		}
		if(dataMap == null) {
			return;
		}
		TextView txtView = _blip.getDocument();
		log.info("Total Commands="+dataMap.size());
		for(Commands cmd : dataMap) {
			if(cmd != null) {
				Range cmdRange = getRange(txtView, cmd.getText());
				txtView.replace(cmdRange, cmd.getValue());
				if(cmd.foundResult()) {
					Range txtRange = new Range();
					txtRange.setStart(cmdRange.getStart());
					txtRange.setEnd(cmdRange.getEnd()- cmd.getLengthToBeSubtracted());
				
					if(cmd.getCommand().equalsIgnoreCase("w")) {
						txtView.setAnnotation( txtRange, "link/manual", (String) cmd.getExternalData());		
					} else if(cmd.getCommand().equalsIgnoreCase("wd")) {
						txtView.replace(txtRange, (String)cmd.getExternalData());
						txtView.setAnnotation(txtRange, "style/fontWeight", "bold");
					}
				}
				log.info("Text=["+cmd.getText()+"] CMD=["+cmd.getCommand()+"] oCMD=["+cmd.getOriginalCommand()+"] Value=["+cmd.getValue()+"]");
				log.info(txtView.getAuthor());
				log.info(txtView.getText());
			}
		}	
	}
	
	private Range getRange(final TextView txtView, String _txt) {
		int sRange = txtView.getText().indexOf(_txt);
		int eRange = sRange + _txt.length();
		Range txtRange = new Range(sRange, eRange);
		return txtRange;
	}	
	
	
	/**
	 * ---------------------------------------------------------------------------------------------------------
	 * [THIS METHOD IS FOR UNIT TESTING ONLY IN THE COMMAND LINE MODE]
	 * ---------------------------------------------------------------------------------------------------------
	 * Process the text. The Original message will be processed in this step.
	 * All the commands (/w) will be removed and the Tokens will be replace with
	 * Token + Wikipedia Link.
	 * 
	 * An updated new Text will be returned.
	 *
	 * @return String text
	 */

	public String processText(String _text, List<Commands> dataMap) {
		String newText = _text;
		isModified = false;
		if(newText != null) {
			newText = newText.replaceAll(regexCommands, "");
			if( dataMap != null) {
				for(Commands cmd : dataMap) {
					if(cmd != null) {
						if(cmd.foundResult()) {
							newText = newText.replace(cmd.getOriginalValue(), cmd.getValue()+" ("+cmd.getExternalData()+") ");
						}
						isModified = true;
					}
				}
			}
		}
		return newText;
	}
	
	/**
	 * For standalone /command line Testing purpose ONLY.
	 * 
	 * @param args
	 */

	public static void main(String[] args) {

		// WikiEngine engine = new WikiEngine(args[0]);
		WikiEngine engine = new WikiEngine(Logger.getLogger("WikiEngine"));
		engine.setSearchQuery("\"Quantum Physics\"");

		System.out.println(engine.getSearchResult().toXMLString());
	}
}
